using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace Pyramid
{
    class ChaikinWithDetail:Multiresolution
    {


        public override ImageWithDetail DownSample(ImageWithDetail dimage)
        {
            int i, j;
            ImageWithDetail rimage = new ImageWithDetail(dimage);
            GImage image = rimage.TheImage;
            double[] r = new double[image.Depth];
            GImage FinalImage = new GImage(image.Width / 2, image.Height / 2);
            GImage DetailImage1 = new GImage(image.Width / 2, image.Height / 2);
            GImage DetailImage2 = new GImage(image.Width / 2, image.Height / 2);
            GImage DetailImage3 = new GImage(image.Width / 2, image.Height / 2);
            
            
            //apply the filter to the columns
            GImage RowImage = new GImage(image.Width / 2, image.Height);

            for (j = 0; j < image.Height; j++)  
            {
              
                

                for (i = 0; i < image.Width; i += 2)
                {
                    //find the lower resulotion pixel value
                    r = Find_NewValue(image, new double[4] { -0.25, 0.75, 0.75, -0.25 }, new int[2] { i, j }, 0);
                    RowImage.SetPixel(i / 2, j, r);
                
                }

            }


            //Find the details
            GImage RowDetailImage = new GImage(image.Width / 2, image.Height);
            for (j = 0; j < image.Height; j++)
            {


                for (i = 0; i < image.Width; i += 2)  
                {
                    //find the corresponding detail
                    r = Find_NewValue(image, new double[4] { 0.25, -0.75, 0.75, -0.25 }, new int[2] { i, j }, 0);
                    RowDetailImage.SetPixel(i / 2,j, r); 
                  
                }

            }


            //reduce the resolution in vertical direction
            for (i = 0; i < RowImage.Width; i++)
            {
             

                

                for (j = 0; j < RowImage.Height; j += 2)  
                {
                    //find the lower resulotion pixel value
                    //the row image
                    r = Find_NewValue(RowImage, new double[4] { -0.25, 0.75, 0.75, -0.25 }, new int[2] { i, j }, 1);
                    FinalImage.SetPixel(i, j / 2, r);
                    //the row detail image
                    r = Find_NewValue(RowDetailImage, new double[4] { -0.25, 0.75, 0.75, -0.25 }, new int[2] { i, j },1);
                    DetailImage2.SetPixel(i, j / 2, r);

                
                }

            }

            //Find the details

            for (i = 0; i < RowImage.Width; i++) 
            {              
               

                for (j = 0; j < RowImage.Height; j += 2)
                {
                    //find the corresponding detail
                    //the row image
                    r = Find_NewValue(RowImage, new double[4] { 0.25, -0.75, 0.75, -0.25 }, new int[2] { i, j }, 1);
                    DetailImage1.SetPixel(i, j / 2, r);
                    //the row detail image
                    r = Find_NewValue(RowDetailImage, new double[4] { 0.25, -0.75, 0.75, -0.25 }, new int[2] { i, j }, 1);
                    DetailImage3.SetPixel(i, j / 2, r);

                  
                }

            }



            rimage.TheImage = FinalImage;

            

            //save the details
            //if (AmplifyDetails) Amp_Factor = 5;
            rimage.TheDetails.Add(Amp_Factor* DetailImage1);//the detail of the final image
            rimage.TheDetails.Add(Amp_Factor * DetailImage2);//the detail image of the row image
            rimage.TheDetails.Add(Amp_Factor * DetailImage3);//the detail of the detail image

            return rimage;
        }
        public override ImageWithDetail UpSample(ImageWithDetail dimage)
        {
            int i, j;
            double[] r1 = new double[dimage.TheImage.Depth];
            double[] r2=new double[dimage.TheImage.Depth];
            GImage image = dimage.TheImage;
            GImage FinalImage = new GImage(image.Width * 2, image.Height * 2);

            //increase the resolution in horizontal direction
            GImage RowImage = new GImage(image.Width, image.Height*2);
            GImage RowDetailImage = new GImage(image.Width , image.Height*2);

            for (i = 0; i < image.Width; i++)
            {
                //perfect reconstruction
                //f1=c1-d1
                for (int k = 0; k < image.Depth;k++ )
                    r1[k] = image.data[i,0,k];

                if (dimage.TheDetails.Count>0)
                    for (int k = 0; k < image.Depth; k++)
                        r2[k] = -dimage.TheDetails[dimage.TheDetails.Count - 3].data[i,0,k];
                else
                {
                    for (int k = 0; k < r2.Length; k++)
                        r2[k] = 0;
                }
                for (int k = 0; k < r2.Length; k++)
                    r1[k] += r2[k];
                RowImage.SetPixel(i, 0, r1);

                //do the same for the detail image if any
                if (dimage.TheDetails.Count>0)
                {
                    //perfect reconstruction
                    //f1=c1-d1
                    for (int k = 0; k < image.Depth; k++)
                        r1[k] = dimage.TheDetails[dimage.TheDetails.Count - 2].data[i, 0, k];

                    if (dimage.TheDetails.Count > 0)
                        for (int k = 0; k < image.Depth; k++)
                            r2[k] = -dimage.TheDetails[dimage.TheDetails.Count - 1].data[i, 0, k];
                    else
                    {
                        for (int k = 0; k < r2.Length; k++)
                            r2[k] = 0;
                    }
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] += r2[k];
                    RowDetailImage.SetPixel(i, 0, r1);
                }
                else
                {
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] = 0;

                    RowDetailImage.SetPixel(i,0, r1);
                   
                }
                //the regular filters
                for (j = 0; j < image.Height - 1; j++)
                {

                    
                    r1 = Find_NewValue(image, new double[2] { 0.75, 0.25 }, new int[2] { i, j }, 1);
                    if (dimage.TheDetails.Count>0)
                        r2 = Find_NewValue(dimage.TheDetails[dimage.TheDetails.Count - 3], new double[2] { 0.75, -0.25 }, new int[2] { i, j }, 1);
                    else
                    {
                        for (int k = 0; k < r2.Length; k++)
                            r2[k] = 0;
                    }
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] += r2[k]/2.0;
                    RowImage.SetPixel(i, 2 * j+1, r1);
                    
                    r1 = Find_NewValue(image, new double[2] { 0.25, 0.75 }, new int[2] { i, j }, 1);
                    if (dimage.TheDetails.Count>0)
                        r2 = Find_NewValue(dimage.TheDetails[dimage.TheDetails.Count - 3], new double[2] { 0.25, -0.75 }, new int[2] { i, j }, 1);
                    else
                    {
                        for (int k = 0; k < r2.Length; k++)
                            r2[k] = 0;
                    }
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] += r2[k]/(-2.0);
                    RowImage.SetPixel(i, 2 * j + 2, r1);
                    if (dimage.TheDetails.Count>0)
                    {
                        //do the same to find the detail row image
                        r1 = Find_NewValue(dimage.TheDetails[dimage.TheDetails.Count - 2], new double[2] { 0.75, 0.25 }, new int[2] { i, j }, 1);
                        r2 = Find_NewValue(dimage.TheDetails[dimage.TheDetails.Count - 1], new double[2] { 0.75, -0.25 }, new int[2] { i, j }, 1);
                        for (int k = 0; k < r2.Length; k++)
                            r1[k] += r2[k]/2.0;

                        RowDetailImage.SetPixel(i, 2 * j+1, r1);


                        r1 = Find_NewValue(dimage.TheDetails[dimage.TheDetails.Count - 2], new double[2] { 0.25, 0.75 }, new int[2] { i, j }, 1);
                        r2 = Find_NewValue(dimage.TheDetails[dimage.TheDetails.Count - 1], new double[2] { 0.25, -0.75 }, new int[2] { i, j }, 1);
                        for (int k = 0; k < r2.Length; k++)
                            r1[k] += r2[k]/(-2.0);
                        RowDetailImage.SetPixel(i, 2 * j + 2, r1);
                    }
                    else
                    {
                        for (int k = 0; k < r2.Length; k++)
                            r1[k] = 0;

                        RowDetailImage.SetPixel(i, 2 * j+1, r1);
                        RowDetailImage.SetPixel(i, 2 * j + 2, r1);
                    }

                }
                //perfect reconstruction
                //fn=cn+dn
                for (int k = 0; k < image.Depth; k++)
                    r1[k] = image.data[i, image.Height-1, k];

                if (dimage.TheDetails.Count>0)
                    for (int k = 0; k < image.Depth; k++)
                        r2[k] = dimage.TheDetails[dimage.TheDetails.Count - 3].data[i, dimage.TheDetails[dimage.TheDetails.Count - 3].Height - 1, k];
                else
                {
                    for (int k = 0; k < r2.Length; k++)
                        r2[k] = 0;
                }
                for (int k = 0; k < r2.Length; k++)
                    r1[k] += r2[k];
                RowImage.SetPixel(i,RowImage.Height-1, r1);

                //do the same for the detail image if any
                if (dimage.TheDetails.Count>0)
                {
                    //perfect reconstruction
                    //f1=c1-d1
                    for (int k = 0; k < image.Depth; k++)
                        r1[k] = dimage.TheDetails[dimage.TheDetails.Count - 2].data[i, dimage.TheDetails[dimage.TheDetails.Count - 2].Height-1, k];

                    if (dimage.TheDetails.Count > 0)
                        for (int k = 0; k < image.Depth; k++)
                            r2[k] = -dimage.TheDetails[dimage.TheDetails.Count - 1].data[i, dimage.TheDetails[dimage.TheDetails.Count - 1].Height - 1, k];
                    else
                    {
                        for (int k = 0; k < r2.Length; k++)
                            r2[k] = 0;
                    }
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] += r2[k];
                    RowDetailImage.SetPixel(i, RowDetailImage.Height-1, r1);
                }
                else
                {
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] = 0;

                    RowDetailImage.SetPixel(i, RowDetailImage.Height-1, r1);

                }

            }
            //increase the resolution in vertical direction
            for (j = 0; j < RowImage.Height; j++) 
            {
                //perfect reconstruction
                //f1=c1-d1
                for (int k = 0; k < RowImage.Depth; k++)
                    r1[k] = RowImage.data[0, j, k];
                
               for (int k = 0; k < image.Depth; k++)
                   r2[k] = -RowDetailImage.data[0,j, k];
             
                for (int k = 0; k < r2.Length; k++)
                    r1[k] += r2[k];
                FinalImage.SetPixel(0,j, r1);
                //regular filters
                for (i = 0; i < RowImage.Width-1; i++)
                {
                    r1 = Find_NewValue(RowImage, new double[2] { 0.75, 0.25 }, new int[2] { i, j }, 0);
                    r2 = Find_NewValue(RowDetailImage, new double[2] { 0.75, -0.25 }, new int[2] { i, j }, 0);
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] += r2[k]/2.0;

                    FinalImage.SetPixel(2 * i+1, j, r1);


                    r1 = Find_NewValue(RowImage, new double[2] { 0.25, 0.75 }, new int[2] { i, j }, 0);
                    r2 = Find_NewValue(RowDetailImage, new double[2] { 0.25, -0.75 }, new int[2] { i, j }, 0);
                    for (int k = 0; k < r2.Length; k++)
                        r1[k] += r2[k]/(-2.0);

                    FinalImage.SetPixel(2 * i + 2, j, r1);
                }
                //perfect reconstruction
                //fn=cn+dn
                for (int k = 0; k < RowImage.Depth; k++)
                    r1[k] = RowImage.data[ RowImage.Width - 1,j, k];

               
               for (int k = 0; k < image.Depth; k++)
                   r2[k] = RowDetailImage.data[RowDetailImage.Width - 1, j,k];
              
                for (int k = 0; k < r2.Length; k++)
                    r1[k] += r2[k];
                FinalImage.SetPixel(FinalImage.Width-1, j, r1);
            }
            //remove the used details
            if (dimage.TheDetails.Count>0)
            {
                dimage.TheDetails.RemoveAt(dimage.TheDetails.Count - 1);
                dimage.TheDetails.RemoveAt(dimage.TheDetails.Count - 1);
                dimage.TheDetails.RemoveAt(dimage.TheDetails.Count - 1);
            }
            dimage.TheImage = FinalImage;
            // dimage.TheImage = RowDetailImage;
            return dimage;
        }
    }
}
